package com.ruoyi.system.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.system.domain.dto.req.DeviceAlarmThresholdReqDTO;
import com.ruoyi.system.domain.dto.resp.DeviceAlarmDetailRespDTO;
import com.ruoyi.system.domain.dto.resp.DeviceAlarmThresholdRespDTO;
import com.ruoyi.system.domain.vo.RealTimeDataVO;
import com.ruoyi.system.enums.ThresholdOperatorEnum;
import com.ruoyi.system.mapper.DeviceAlarmThresholdMapper;
import com.ruoyi.system.service.AgricultureService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class AgricultureServiceImpl implements AgricultureService {
    @Resource
    MqttClient mqttClient;

    @Autowired
    private RedisCache redisCache;

    @Value("${mqtt.topic.name}")
    private String topicName;

    @Autowired
    private DeviceAlarmThresholdMapper deviceAlarmThresholdMapper;
    @Override
    public void subScribe() throws MqttException {
        mqttClient.subscribe(topicName);
    }

// 模拟不同的传感器数据
double temperature = Math.random() * 30 + 15; // 温度范围 15-45°C
    double humidity = Math.random() * 50 + 30;     // 湿度范围 30-80%
    double lightIntensity = Math.random() * 1000;   // 光照强度范围 0-1000 lux
    double soilMoisture = Math.random();          // 土壤水分含量范围 0-1
    double soilPH = Math.random() * 14;           // 土壤pH值范围 0-14
    double soilEC = Math.random() * 2;            // 土壤电导率范围 0-2 mS/cm


    // 使用线程安全队列保存历史数据
    private static final Deque<Double> temperatureActualDeque = new ConcurrentLinkedDeque<>();
    private static final Deque<Double> temperatureExpectedDeque = new ConcurrentLinkedDeque<>();
    private static final int HISTORY_SIZE = 10;
    @Override
    public RealTimeDataVO getTempHistory() {
        String realTimeKey = "agriculture:iot:device01:real_time";
        JSONObject rawData = redisCache.getCacheObject(realTimeKey);
        Double temperatureExpected = rawData.getDouble("temperatureExpected");
        Double temperatureActual = rawData.getDouble("temperatureActual");

        String currentTime = rawData.getString("currentTime");
        currentTime = currentTime.substring(11, currentTime.length());
        RealTimeDataVO data = new RealTimeDataVO();
        // 温室常见温度范围 /温度范围 15-45°C
        data.setTemperatureActual(temperatureActual);
        data.setTemperatureExpected(temperatureExpected);
        // 维护温度历史数据队列
        synchronized(temperatureActualDeque){
            temperatureActualDeque.addLast(data.getTemperatureActual());
            if(temperatureActualDeque.size() > HISTORY_SIZE){
                temperatureActualDeque.removeFirst();
            }
        }
        synchronized(temperatureExpectedDeque){
            temperatureExpectedDeque.addLast(data.getTemperatureExpected());
            if(temperatureExpectedDeque.size() > HISTORY_SIZE){
                temperatureExpectedDeque.removeFirst();
            }
        }
        data.setTemperatureActualList(new ArrayList<>(temperatureExpectedDeque));
        data.setTemperatureExpectedList(new ArrayList<>(temperatureExpectedDeque));
        data.setCurrentTime(currentTime);
        return data;
    }

    @Override
    public RealTimeDataVO getRealTimeData() {
        String realTimeKey = "agriculture:iot:device01:real_time";
        JSONObject rawData = redisCache.getCacheObject(realTimeKey);
        Double temperatureExpected = rawData.getDouble("temperatureExpected");
        Double temperatureActual = rawData.getDouble("temperatureActual");
        Double humidity = rawData.getDouble("humidity");
        Double soilMoisture = rawData.getDouble("soil_moisture");
        Double soilPH = rawData.getDouble("soil_ph");
        Double soilConductivity = rawData.getDouble("soil_conductivity");
        Double lightIntensity = rawData.getDouble("light_intensity");
        Double co2 = rawData.getDouble("co2");
        String currentTime = rawData.getString("currentTime");
        currentTime = currentTime.substring(11, currentTime.length());
        RealTimeDataVO data = new RealTimeDataVO();

        // 生成实时数据（带合理波动）
        // 温室常见温度范围 /温度范围 15-45°C
        data.setTemperatureActual(temperatureActual);
        data.setTemperatureExpected(temperatureExpected);
        // 相对湿度范围 / 湿度范围 30-80%
        data.setHumidity(humidity);
        // 土壤湿度范围 /土壤水分含量范围 0-1
        data.setSoilMoisture(soilMoisture);
        // 常见农作物适宜pH范围 /土壤pH值范围 0-14
        data.setSoilPH(soilPH);
        // 电导率范围 /土壤电导率范围 0-2 mS/cm
        data.setSoilConductivity(soilConductivity);
        // 光照强度范围 /光照强度范围 0-1000 lux
        data.setLightIntensity(lightIntensity);
        // CO2浓度范围
        data.setCo2(co2);
        List<DeviceAlarmDetailRespDTO> alarmList = new ArrayList<>();
        DeviceAlarmDetailRespDTO deviceAlarmDetailRespDTO;
        DeviceAlarmThresholdReqDTO reqDTO = new DeviceAlarmThresholdReqDTO();
        reqDTO.setVariableName("temperatureActual");
        List<DeviceAlarmThresholdRespDTO> list = deviceAlarmThresholdMapper.list(reqDTO);
        if (!CollectionUtils.isEmpty(list)) {
            for (DeviceAlarmThresholdRespDTO item : list){
                // 实时的新值
                Object newValue = rawData.get(item.getVariableName());
                // 比较新值与配置值大小
                boolean needAlarm = ThresholdOperatorEnum.apply(item.getOperType(), newValue, item.getThresholdValue());
                log.info("configValue is {}, newValue is {} compare result: " + needAlarm, item.getThresholdValue(), newValue);
                if (!needAlarm) {
                    continue;
                }

                if (isAlarmCircle(item.getDeviceSn(), item.getVariableName(), item.getAlarmCycle())) {
                    deviceAlarmDetailRespDTO = new DeviceAlarmDetailRespDTO();
                    // 告警返回
                    deviceAlarmDetailRespDTO.setDeviceSn(item.getDeviceSn());
                    deviceAlarmDetailRespDTO.setVariableName(DictUtils.getDictLabel("monitor_variable", item.getVariableName()));
                    deviceAlarmDetailRespDTO.setAlarmThresholdType(DictUtils.getDictLabel("threshold_type", item.getAlarmThresholdType()));
                    deviceAlarmDetailRespDTO.setThresholdValue(item.getThresholdValue());
                    alarmList.add(deviceAlarmDetailRespDTO);
                }
            }
            data.setAlarmList(alarmList);
        }


//        data.setTemperature(generateValue(15.0, 35.0, 1.5));       // 温室常见温度范围
//        data.setHumidity(generateValue(30.0, 80.0, 3.0));          // 相对湿度范围
//        data.setSoilMoisture(generateValue(20.0, 60.0, 2.0));      // 土壤湿度范围
//        data.setSoilPH(generateValue(5.5, 7.5, 0.1));              // 常见农作物适宜pH范围
//        data.setSoilConductivity(generateValue(200.0, 1500.0, 50.0)); // 电导率范围
//        data.setLightIntensity(generateValue(0.0, 10000.0, 500.0));// 光照强度范围
//        data.setCo2(generateValue(300.0, 2000.0, 50.0));           // CO2浓度范围
        return data;
    }

    // 带波动值的随机生成方法
    private Double generateValue(Double min, Double max, Double fluctuation) {
        double base = min + (Math.random() * (max - min));
        return Math.round((base + (Math.random()*2-1)*fluctuation) * 10) / 10.0;
    }

    /**
     * 判断是否在告警周期内
     * @param deviceId
     * @param variableName
     * @param thresholdAlarmCircle
     * @return
     */
    private Boolean isAlarmCircle(String deviceId, String variableName, Integer thresholdAlarmCircle) {
        long currentTime = System.currentTimeMillis();
        String alarmCircleKey = String.format("agriculture:iot:%s:%s:alarm_last_send_time",deviceId, variableName);
        Object object = redisCache.getCacheObject(alarmCircleKey);
        if (object != null) {
            return false;
        }
        redisCache.setCacheObject(alarmCircleKey, currentTime, thresholdAlarmCircle, TimeUnit.SECONDS);
        return true;
    }
}
